<?php
declare(strict_types=1);

namespace App\Interface\Pay;

use App\Constants\ErrorCode;
use App\Interface\PaymentStrategyInterface;
use App\Model\Contract;
use App\Model\Coupon;
use App\Model\Member;
use App\Model\MemberCoupon;
use App\Model\Normal;
use App\Model\Order;
use App\Model\OrderType;
use App\Model\Special;
use App\Services\Wechat;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Utils\Str;
use Hyperf\Redis\Redis;
use Trait\ResponseMsg;
use Util\Common;
use Yansongda\Pay\Pay;
use Hyperf\Logger\LoggerFactory;
use Psr\Log\LoggerInterface;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;

class WeChatPaymentStrategy implements PaymentStrategyInterface
{

    use ResponseMsg;

    protected array $config;
    protected RequestInterface $request;
    private $logger;

    #[Inject]
    protected OrderType $orderType;

    #[Inject]
    protected Member $member;

    public function __construct(RequestInterface $request)
    {

        $this->request = $request;
        $this->config = config('pay');
        $log_name ='wxpay'.date('Y-m-d',time());
        $loggerFactory = container()->get(\Hyperf\Logger\LoggerFactory::class);
        $this->logger = $loggerFactory->get($log_name,'wxpay');
    }

    public function __call($name, $arguments)
    {
        // 处理不存在的实例方法调用
        return '';
    }

    /**支付-通用
     * @param array $data
     * @return mixed
     */
    public function pay(array $data)
    {
        $class = Str::studly($data['type_mode']);
        return $this->$class($data);
    }

    /**从订单列表去支付
     * @param array $data
     * @return mixed
     */
    public function payNow(array $params)
    {
        $orderModel = Order::where('order_no', $params['order_no'])->first();
        if (!$orderModel) {
            return array('code' => ErrorCode::ORDER_NOT_FOUND, 'msg' => '订单不存在');
        }
        if ($orderModel->status != Order::NORMAL) {
            return array('code' => ErrorCode::ORDER_NOT_FOUND, 'msg' => '订单不可被支付');
        }
        if (isset($params['coupon_id']) && $params['coupon_id'] > 0 && !$orderModel->member_coupon_id) {
            $realArray = $this->useConpou((int)$params['coupon_id'], (int)$orderModel->money);
            if ($realArray['code'] != 200) {
                return $realArray;
            }
            $orderModel->member_coupon_id = $params['coupon_id'];
            $real_money = $realArray['real_money'];
        } else {
            $real_money = $orderModel->money;
        }
        $orderModel->real_money = $real_money;
        $orderModel->version += 1;
        $orderModel->save();
        $class = Str::studly($params['type_mode']);
        $params['payid'] = $orderModel->type_id;
        return $this->$class($params);
    }

    /**微信环境列表去支付
     * @param $orderModel
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function mpNow($orderModel, array $params): array
    {
        if (!isset($params['code'])) {
            return array('code' => ErrorCode::PARAMS_ERR, 'msg' => '缺失参数code');
        }
        $result = Wechat::getOpenId($params['code']);
        if (!isset($result['openid'])) {
            return array('code' => ErrorCode::PARAMS_ERR, 'msg' => '获取openid失败');
        }
        $order = [
            'out_trade_no' => $orderModel->order_no . '-' . $orderModel->version,
            'description' => $orderModel->title,
            'amount' => [
                'total' => $orderModel->real_money,
            ],
            'payer' => [
                'openid' => $result['openid'],
            ],
//            'scene_info' => [
//                'payer_client_ip' => HomeTool::get_real_ip(),
//                'h5_info' => [
//                    'type' => 'Wap',
//                 ]
//              ]
        ];
        $result = Pay::wechat($this->config)->mp($order)->toArray();
        return array('data' => $result, 'code' => ErrorCode::SUCCESS, 'order' => $order);
    }

    /**手机端从列表去支付
     * @param $orderModel
     * @param array $params
     * @return array
     */
    public function wapNow($orderModel, array $params): array
    {
        $order = [
            'out_trade_no' => $orderModel->order_no . '-' . $orderModel->version,
            'description' => $orderModel->title,
            'amount' => [
                'total' => $orderModel->real_money,
            ],
            'scene_info' => [
                'payer_client_ip' => Normal::getRealIp($this->request),
                'h5_info' => [
                    'type' => 'Wap',
                ]
            ]
        ];
        $result = Pay::wechat($this->config)->wap($order)->toArray();
        return array('data' => $result, 'code' => ErrorCode::SUCCESS, 'order' => $order);
    }

    /**根据订单号设置二维码
     * @param $params
     * @return array
     */
    public function qrCode($params)
    {

        $orderModel = Order::where('order_no', $params['order_no'])->first();
        if (!$orderModel) {
            return array('code' => ErrorCode::ORDER_NOTFOUND);
        }
        if ($orderModel->status != Order::NORMAL) {
            return array('code' => ErrorCode::ORDER_CANT_PAY);
        }
        if ($params['coupon_id'] && !$orderModel->member_coupon_id) {
            $orderModel->member_coupon_id = $params['coupon_id'];
            $moneyArray = $this->useConpou((int)$params['coupon_id'], $orderModel->money);
            if ($moneyArray['code'] != 200) {
                return $moneyArray;
            }
            $orderModel->real_money = $moneyArray['real_money'];
        }
        $orderModel->order_no = $params['order_no'];
        $orderModel->version += 1;
        $orderModel->save();
        $order = [
            'out_trade_no' => $params['order_no'] . '-' . $orderModel->version,
            'description' => $orderModel->title,
            'amount' => [
                'total' => $orderModel->real_money,
            ],
        ];
        try {
            $pay = Pay::wechat($this->config)->scan($order);
            if(isset($pay['code_url'])){
                $qrCode = QrCode::create($pay['code_url']);
                $writer = new PngWriter();
                $result = $writer->write($qrCode);
                $pay = $result->getDataUri();


            }
            return array('code' => 200, 'data' => $pay, 'order_info' => $order);
        } catch (\Exception $e) {
            return array('code' => ErrorCode::OPTION_ERR, 'message' => $e->getMessage());
        }

    }

    /**电脑端微信生成订单
     * @param array $params
     * @return array
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function pcPay(array $params): array
    {
        $params = $this->filterParams($params);
        $order_no = Order::generateOrderNumber();
        $member = $this->member->getInfo($this->request);
        if(!$member){
            $member['id'] = 0;
        }
        if (isset($params['price'])) {
            $real_money = $params['price'];
            $model = Order::create([
                'title' => '升级终身会员',
                'type' => OrderType::UP_VIP,
                'member_id' => $member['id'],
                'money' => $real_money,
                'real_money' => $real_money,
                'order_no' => $order_no,
                'pay_type' => Common::ORDER_WX_PAY,
                'status' => Order::NORMAL,
                'bd_userid' => $params['userid'],
                'bd_planid' => $params['planid'],
                'bd_unitid' => $params['unitid'],
                'bd_keywordid' => $params['keywordid'],
                'version' => 1,
                'pay_ip' => Normal::getRealIp($this->request),
            ]);
        } elseif (isset($params['special_id'])) {
            $specialModel = Special::where('id', $params['special_id'])
                ->first();
            if (!$specialModel) {
                return array('code' => ErrorCode::PARAMS_ERR);
            }
            $real_money = $specialModel->price;
            $model = Order::create([
                'title' => '觅合同-购买专题',
                'type' => OrderType::SPECIAL,
                'member_id' => $member['id'] ?? null,
                'money' => $real_money,
                'real_money' => $real_money,
                'order_no' => $order_no,
                'pay_type' => Common::ORDER_WX_PAY,
                'status' => Order::NORMAL,
                'bd_userid' => $params['userid'],
                'bd_planid' => $params['planid'],
                'bd_unitid' => $params['unitid'],
                'bd_keywordid' => $params['keywordid'],
                'version' => 1,
                'pay_ip' => Normal::getRealIp($this->request),
                'special_id' => $specialModel->id,
                'keyword' => $params['keyword'],
                'bd_vid' => $params['bd_vid']
            ]);
        } else {
            $pay_info = $this->orderType->where('id', $params['payid'])->first();
            if ($pay_info->type == OrderType::NORMAL_TYPE && !$member) {
                return array('code' => ErrorCode::UNKNOW_INFO);
            }
            if ($pay_info->type == OrderType::RECEIVING) {
                if (!$this->validateReceiving($params)) {
                    return array('code' => ErrorCode::INVALID_RECEIVING);
                }
            }
            if (isset($params['coupon_id']) && $params['coupon_id'] > 0 && $pay_info->type < OrderType::UP_VIP) {
                $realArray = $this->useConpou((int)$params['coupon_id'], (int)$pay_info['price']);
                if ($realArray['code'] != 200) {
                    return $realArray;
                }
                $real_money = $realArray['real_money'];
            } else {
                $params['coupon_id'] = null;
                $real_money = $pay_info->price;
            }
            $model = Order::create([
                'title' => $pay_info->tag,
                'type_id' => $pay_info->id,
                'type' => $pay_info->type,
                'member_id' => $member['id'] ?? null,
                'money' => $pay_info->price,
                'real_money' => $real_money,
                'order_no' => $order_no,
                'pay_type' => Common::ORDER_WX_PAY,
                'member_coupon_id' => $params['coupon_id'],
                'status' => Order::NORMAL,
                'receiving' => $params['receiving'] ?? null,
                'bd_userid' => $params['userid'],
                'bd_planid' => $params['planid'],
                'bd_unitid' => $params['unitid'],
                'bd_keywordid' => $params['keywordid'],
                'bd_vid' => $params['bd_vid'],
                'version' => 1,
                'pay_ip' => Normal::getRealIp($this->request),
                'contract_id' => $params['contract_id'] ?? 0,
                'keyword' => $params['keyword']
            ]);
        }
        if ($model) {
            return array('code' => 200, 'order_no' => $order_no);
        }
        return array('code' => ErrorCode::OPTION_ERR, 'msg' => '操作繁忙');
    }


    /**微信回调
     * @return void
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \Yansongda\Pay\Exception\ContainerException
     * @throws \Yansongda\Pay\Exception\InvalidParamsException
     */
    public function notify()
    {
        $pay = Pay::wechat($this->config);
        $data = $pay->callback($this->request);
        if ($data['summary'] == '支付成功') {
            $order_no = explode('-', $data['resource']['ciphertext']['out_trade_no']);
            Order::handle($order_no[0],
                $data['resource']['ciphertext']['transaction_id'],
                $data['resource']['ciphertext']['payer']['openid']);
        }
        $this->logger->info(json_encode($data));
        return '';
    }

    /**同步取消微信订单
     * @param string $order_no
     * @return void
     * @throws \Yansongda\Pay\Exception\ContainerException
     * @throws \Yansongda\Pay\Exception\InvalidParamsException
     * @throws \Yansongda\Pay\Exception\ServiceNotFoundException
     */
    public function cancel(string $order_no)
    {
        $order_close = [
            'out_trade_no' => $order_no,
        ];
        Pay::wechat($this->config)->close($order_close);
        return '';
    }


    /**h5支付
     * @param array $params
     * @return array
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function wapPay(array $params): array
    {
        $params = $this->filterParams($params);
        $order_no = Order::generateOrderNumber();
        $member = $this->member->getInfo($this->request);
        if (isset($params['price'])) {
            $real_money = $params['price'];
            $payTag = '升级终身会员';
            $model = Order::create([
                'title' => $payTag,
                'type' => OrderType::UP_VIP,
                'member_id' => $member['id'],
                'money' => $real_money,
                'real_money' => $real_money,
                'order_no' => $order_no,
                'pay_type' => Common::ORDER_WX_PAY,
                'status' => Order::NORMAL,
                'bd_userid' => $params['userid'],
                'bd_planid' => $params['planid'],
                'bd_unitid' => $params['unitid'],
                'bd_keywordid' => $params['keywordid'],
                'version' => 1,
                'pay_ip' => Normal::getRealIp($this->request),
            ]);
        } elseif (isset($params['special_id']) && $params['special_id'] > 0) {
            $specialModel = Special::where('id', $params['special_id'])
                ->first();
            if (!$specialModel) {
                return array('code' => ErrorCode::PARAMS_ERR, 'msg' => '专题数据不存在');
            }
            $real_money = $specialModel->price;
            $payTag = '觅合同-购买专题';
            $model = Order::create([
                'title' => $payTag,
                'type' => OrderType::SPECIAL,
                'member_id' => $member['id'] ?? null,
                'money' => $real_money,
                'real_money' => $real_money,
                'order_no' => $order_no,
                'pay_type' => Common::ORDER_WX_PAY,
                'status' => Order::NORMAL,
                'bd_userid' => $params['userid'],
                'bd_planid' => $params['planid'],
                'bd_unitid' => $params['unitid'],
                'bd_keywordid' => $params['keywordid'],
                'version' => 1,
                'pay_ip' => Normal::getRealIp($this->request),
                'special_id' => $specialModel->id,
                'keyword' => $params['keyword'],
                'bd_vid' => $params['bd_vid']
            ]);
        } else {
            $pay_info = $this->orderType->where('id', $params['payid'])->first();
            if ($pay_info->type == OrderType::NORMAL_TYPE && !$member) {
                return array('code' => ErrorCode::UNKNOW_INFO);
            }
            if ($pay_info->type == OrderType::RECEIVING) {
                if (!$this->validateReceiving($params)) {
                    return array('code' => ErrorCode::INVALID_RECEIVING);
                }
            }
            if (isset($params['coupon_id']) && $params['coupon_id'] > 0 && $pay_info->type < OrderType::UP_VIP) {
                $realArray = $this->useConpou((int)$params['coupon_id'], (int)$pay_info['price']);
                if ($realArray['code'] != 200) {
                    return $realArray;
                }
                $real_money = $realArray['real_money'];
            } else {
                $params['coupon_id'] = null;
                $real_money = $pay_info->price;
            }
            $payTag = $pay_info->tag;
            $model = Order::create([
                'title' => $payTag,
                'type_id' => $pay_info->id,
                'type' => $pay_info->type,
                'member_id' => $member['id'] ?? null,
                'money' => $pay_info->price,
                'member_coupon_id' => $params['coupon_id'],
                'real_money' => $real_money,
                'order_no' => $order_no,
                'pay_type' => Common::ORDER_WX_PAY,
                'status' => 0,
                'receiving' => $params['receiving'] ?? null,
                'bd_userid' => $params['userid'],
                'bd_planid' => $params['planid'],
                'bd_unitid' => $params['unitid'],
                'bd_keywordid' => $params['keywordid'],
                'bd_vid' => $params['bd_vid'],
                'version' => 1,
                'pay_ip' => Normal::getRealIp($this->request),
                'contract_id' => $params['contract_id'] ?? 0,
                'keyword' => $params['keyword']
            ]);
        }
        if ($model) {
            $order = [
                'out_trade_no' => $order_no . '-1',
                'description' => $payTag,
                'amount' => [
                    'total' => $real_money,
                ],

                'scene_info' => [
                    'payer_client_ip' => Normal::getRealIp($this->request),
                    'h5_info' => [
                        'type' => 'Wap',
                    ]
                ]
            ];
            $result = Pay::wechat($this->config)->wap($order)->toArray();
            return array('code' => 200, 'data' => $result, 'order'=>$order_no);
        }
        return array('code' => ErrorCode::OPTION_ERR);
    }

    /**微信jsapi支付
     * @param array $params
     * @return array
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function mpPay(array $params): array
    {
        $params = $this->filterParams($params);
        if (!$params['code']) {
            return array('code' => ErrorCode::ORDER_INVALID_CODE);
        }
        $result = Wechat::getOpenId($params['code']);
        if (!isset($result['openid'])) {
            return array('code' => ErrorCode::GET_OPENID_ERROR);
        }
        $order_no = Order::generateOrderNumber();
        $member = $this->member->getInfo($this->request);
        if (isset($params['price'])) {
            $real_money = $params['price'];
            $payTag = '升级终身会员';
            $model = Order::create([
                'title' => $payTag,
                'type' => OrderType::UP_VIP,
                'member_id' => $member['id'],
                'money' => $real_money,
                'real_money' => $real_money,
                'order_no' => $order_no,
                'pay_type' => Common::ORDER_WX_PAY,
                'status' => Order::NORMAL,
                'bd_userid' => $params['userid'],
                'bd_planid' => $params['planid'],
                'bd_unitid' => $params['unitid'],
                'bd_keywordid' => $params['keywordid'],
                'version' => 1,
                'pay_ip' => Normal::getRealIp($this->request),
            ]);
        } elseif (isset($params['special_id']) && $params['special_id'] > 0) {
            $specialModel = Special::where('id', $params['special_id'])
                ->first();
            if (!$specialModel) {
                return array('code' => ErrorCode::PARAMS_ERR, 'msg' => '专题数据不存在');
            }
            $real_money = $specialModel->price;
            $payTag = '觅合同-购买专题';
            $model = Order::create([
                'title' => $payTag,
                'type' => OrderType::SPECIAL,
                'member_id' => $member['id'] ?? null,
                'money' => $real_money,
                'real_money' => $real_money,
                'order_no' => $order_no,
                'pay_type' => Common::ORDER_WX_PAY,
                'status' => Order::NORMAL,
                'bd_userid' => $params['userid'],
                'bd_planid' => $params['planid'],
                'bd_unitid' => $params['unitid'],
                'bd_keywordid' => $params['keywordid'],
                'version' => 1,
                'pay_ip' => Normal::getRealIp($this->request),
                'special_id' => $specialModel->id,
                'keyword' => $params['keyword'],
                'bd_vid' => $params['bd_vid']
            ]);
        } else {
            $pay_info = $this->orderType->where('id', $params['payid'])->first();
            if ($pay_info->type == OrderType::NORMAL_TYPE && !$member) {
                return array('code' => ErrorCode::UNKNOW_INFO);
            }
            if ($pay_info->type == OrderType::RECEIVING) {
                if (!$this->validateReceiving($params)) {
                    return array('code' => ErrorCode::INVALID_RECEIVING);
                }
            }
            if (isset($params['coupon_id']) && $params['coupon_id'] > 0 && $pay_info->type < OrderType::UP_VIP) {
                $realArray = $this->useConpou((int)$params['coupon_id'], (int)$pay_info['price']);
                if ($realArray['code'] != 200) {
                    return $realArray;
                }
                $real_money = $realArray['real_money'];
            } else {
                $params['coupon_id'] = null;
                $real_money = $pay_info->price;
            }
            $payTag = $pay_info->tag;
            $model = Order::create([
                'title' => $payTag,
                'type_id' => $pay_info->id,
                'type' => $pay_info->type,
                'member_id' => $member['id'] ?? null,
                'money' => $pay_info->price,
                'real_money' => $real_money,
                'member_coupon_id' => $params['coupon_id'],
                'order_no' => $order_no,
                'pay_type' => Common::ORDER_WX_PAY,
                'status' => 0,
                'receiving' => $params['receiving'] ?? null,
                'bd_userid' => $params['userid'],
                'bd_planid' => $params['planid'],
                'bd_unitid' => $params['unitid'],
                'bd_keywordid' => $params['keywordid'],
                'bd_vid' => $params['bd_vid'],
                'version' => 1,
                'pay_ip' => Normal::getRealIp($this->request),
                'contract_id' => $params['contract_id'] ?? null,
                'keyword' => $params['keyword']
            ]);
        }
        if ($model) {
            $order = [
                'out_trade_no' => $order_no . '-1',
                'description' => $payTag,
                'amount' => [
                    'total' => $real_money,
                ],
                'payer' => [
                    'openid' => $result['openid'],
                ],
            ];
            $result = Pay::wechat($this->config)->mp($order)->toArray();
            return array('code' => 200, 'data' => $result, 'order' => $order);
        }
        return array('code' => ErrorCode::OPTION_ERR);
    }

    /**整理参数
     * @param array $params
     * @return array
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function filterParams(array $params): array
    {
        if (!array_key_exists('bd_vid', $params)) {
            $params['bd_vid'] = null;
        }
        if (!array_key_exists('keyword', $params)) {
            $params['keyword'] = null;
        }
        if (!array_key_exists('userid', $params)) {
            $params['userid'] = null;
        }
        if (!array_key_exists('planid', $params)) {
            $params['planid'] = null;
        }
        if (!array_key_exists('unitid', $params)) {
            $params['unitid'] = null;
        }
        if (!array_key_exists('keywordid', $params)) {
            $params['keywordid'] = null;
        }
        if (isset($params['contract_id']) && $params['contract_id']) {
            $title = Contract::where('id', $params['contract_id'])->value('title');
            redis()->zincrby(Common::ORDER_RANK_LIST, 1, $title);
        }else{
            $params['contract_id'] = 0;
        }
        return $params;
    }

    /**验证定制合同表单内容
     * @param $params
     * @return bool
     */
    public function validateReceiving($params): bool
    {
        if (isset($params['receiving'])) {
            if (!is_array($params['receiving'])) {
                $params['receiving'] = json_decode($params['receiving'], true);
            }
            if (!$params['receiving']['name']) {
                return false;
            }
            if (!$params['receiving']['phone'] && !$params['receiving']['email']) {
                return false;
            }
            return true;
        }
        return false;
    }


    /**使用优惠券
     * @param int $couponId
     * @param $payInfo
     * @return array
     */
    public function useConpou(int $couponId, int $payPrice): array
    {
        $MemberCoupon = MemberCoupon::where('id', $couponId)->first();
        $coupon = Coupon::where('id', $MemberCoupon->coupon_id)->first();
        if ($payPrice < $coupon->fill_price) {
            return array('code' => ErrorCode::ORDER_COUPON_ERROR);
        }
        $now = time();
        if ($MemberCoupon->status != MemberCoupon::NORMAL || $now > $MemberCoupon->end) {
            return array('code' => ErrorCode::ORDER_COUPON_ERROR);
        }
        $MemberCoupon->status = MemberCoupon::USED;
        $MemberCoupon->save();
        return array('code' => ErrorCode::SUCCESS, 'real_money' => $payPrice - $coupon->price);
    }


}
